/*************************************************************************
 *
 *   file		: SkillHandler.cs
 *   copyright		: (C) The WCell Team
 *   email		: info@wcell.org
 *   last changed	: $LastChangedDate: 2009-09-03 10:41:48 +0100 (Thu, 03 Sep 2009) $
 *   last author	: $LastChangedBy: dominikseifert $
 *   revision		: $Rev: 1073 $
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *************************************************************************/

using System;
using System.Collections.Generic;
using NLog;
using WCell.Constants;
using WCell.Constants.Skills;
using WCell.Constants.Spells;
using WCell.Constants.Updates;
using WCell.Core;
using WCell.Core.DBC;
using WCell.Core.Initialization;
using WCell.Core.Network;
using WCell.RealmServer.Chat;
using WCell.RealmServer.Network;
using WCell.RealmServer.Spells;
using WCell.Util;
using WCell.RealmServer.Content;

namespace WCell.RealmServer.Skills
{
	// TODO: Send initial spells for profession skills
	// TODO: Handle improving of skills
	public static partial class SkillHandler
	{
		public const uint MaxSkillId = 2000;

		private static Logger log = LogManager.GetCurrentClassLogger();


		#region Global Skill-Settings
		/// <summary>
		/// The maximum amount of skills allowed (limited by the amount of PlayerUpdateFields for skills)
		/// </summary>
		public const int MaxAmount = 128;
		public const PlayerFields HighestField = PlayerFields.SKILL_INFO_1_1 + (MaxAmount * 3);

		/// <summary>
		/// The max amount of professions that every player may learn (Blizzlike: 2)
		/// </summary>
		public static uint MaxProfessionsPerChar = 2;

		/// <summary>
		/// Whether to automatically remove all spells that belong to a skill when removing it.
		/// </summary>
		public static bool RemoveAbilitiesWithSkill = true;
		#endregion


		#region Skill-Containers
		/// <summary>
		/// All skills, indexed by their id
		/// </summary>
		public static readonly SkillLine[] ById = new SkillLine[MaxSkillId];

		/// <summary>
		/// All lists of all Race/Class-specific skillinfos: Use RaceClassInfos[race][class]
		/// </summary>
		public static readonly Dictionary<SkillId, SkillRaceClassInfo>[][] RaceClassInfos =
			new Dictionary<SkillId, SkillRaceClassInfo>[WCellDef.RaceTypeLength][];

		/// <summary>
		/// All SkillAbility-lists, indexed by their SkillId
		/// </summary>
		public static readonly SkillAbility[][] AbilitiesBySkill = new SkillAbility[MaxSkillId][];


		//public static readonly Dictionary<SpellId, List<SkillAbility>> AbilitiesBySpellId = new Dictionary<SpellId, List<SkillAbility>>(1000);
		// public static readonly Dictionary<SpellId, SkillLine> AbilitiesBySpellId = new Dictionary<SpellId, SkillLine>(1000);
		#endregion


		#region Init
		static DBCReader<SkillTier, SkillTierConverter> TierReader;
		static DBCReader<SkillRaceClassInfo, SkillRaceClassInfoConverter> RaceClassReader;

		[Initialization(InitializationPass.Second, "Initialize Skills")]
		public static void Initialize()
		{
			TierReader = new DBCReader<SkillTier, SkillTierConverter>(RealmServerConfiguration.GetDBCFile("SkillTiers.dbc"));

			var lineReader =
				new DBCReader<SkillLine, SkillLineConverter>(RealmServerConfiguration.GetDBCFile("SkillLine.dbc"));

			// make sure that all these skill types have correct tiers
			foreach (var line in lineReader.Entries.Values)
			{
				ById[(uint)line.Id] = line;

				/*if ((line.Category == SkillCategory.WeaponSkill && line.Id != SkillId.DualWield) ||
					line.Category == SkillCategory.ClassSkill ||
					line.Category == SkillCategory.Profession ||
					line.Category == SkillCategory.SecondarySkill)
				{

					line.Tier = new SkillTier(SkillTier.DefaultTier, line.Id);
				}
				else*/
				if (line.Category == SkillCategory.Language)
				{
					var lang = LanguageHandler.GetLanguageDescBySkillType(line.Id);
					if (lang != null)
					{
						line.Language = lang.Language;
					}
				}
			}

			RaceClassReader = new DBCReader<SkillRaceClassInfo, SkillRaceClassInfoConverter>(
				RealmServerConfiguration.GetDBCFile("SkillRaceClassInfo.dbc"));


		    var abilityReader =
		        new DBCReader<SkillAbility, SkillAbilityConverter>(
		            RealmServerConfiguration.GetDBCFile("SkillLineAbility.dbc"));

			var abilityLists = new List<SkillAbility>[MaxSkillId];
			foreach (var ability in abilityReader.Entries.Values)
			{
				// why would the spell be null?
				if (ability.Spell != null)
				{
					// some skills link the same spell multiple times
					//if (!AbilitiesBySpellId.ContainsKey((SpellId)ability.Spell.Id)) {
					//AbilitiesBySpellId.Add((SpellId)ability.Spell.Id, ability.SkillLine);
					//}
					ability.Spell.Ability = ability;
					ability.Spell.Skill = ability.Skill;
				}
				else
				{
					// Don't know whats wrong here?
				}

				// create a doubly linked list
				if (ability.NextSpellId > 0)
				{
					var nextSpell = SpellHandler.Get(ability.NextSpellId);
					if (nextSpell != null)
					{
						ability.NextAbility = nextSpell.Ability;
						if (nextSpell.Ability != null)
						{
							nextSpell.Ability.PreviousAbility = ability;
						}
					}
				}

				// initial ability to a skill
				//if (ability.AcquireMethod == SkillAcquireMethod.OnLearningSkill)
				//{
				//    ability.Skill.InitialAbilities.Add(ability);
				//}

				List<SkillAbility> abilities = abilityLists.Get((uint)ability.Skill.Id);

				if (abilities == null)
				{
					abilities = new List<SkillAbility>();

					ArrayUtil.Set(ref abilityLists, (uint)ability.Skill.Id, abilities);
				}

				abilities.Add(ability);
			}

			for (int i = 0; i < abilityLists.Length; i++)
			{
				if (abilityLists[i] != null)
				{
					AbilitiesBySkill[i] = abilityLists[i].ToArray();
				}
			}

			//FindSpells();
		}

		private static void FindSpells()
		{
			foreach (var spell in SpellHandler.ById)
			{
				if (spell != null)
				{
					if (spell.SkillId != SkillId.None)
					{
						var skill = ById[(uint)spell.SkillId];
						skill.Spell = spell;
						spell.Skill = skill;
					}
				}
			}
		}
		#endregion


		public static SkillLine Get(SkillId id)
		{
			if ((uint)id >= ById.Length)
			{
				return null;
			}
			return ById[(uint)id];
		}

		public static SkillLine Get(uint id)
		{
			if (id >= ById.Length)
			{
				return null;
			}
			return ById[id];
		}

		public static SkillAbility[] GetAbilities(SkillId id)
		{
			if ((uint)id >= AbilitiesBySkill.Length)
			{
				return null;
			}
			return AbilitiesBySkill[(uint)id];
		}

		public static SkillAbility[] GetAbilities(uint id)
		{
			if (id >= AbilitiesBySkill.Length)
			{
				return null;
			}
			return AbilitiesBySkill[id];
		}

		public static SkillAbility GetAbility(SkillId skill, SpellId spell)
		{
			var abilities = GetAbilities(skill);
			return Array.Find(abilities, ability => ability.Spell.SpellId == spell);
		}

		public static SkillId GetSkill(SkinningType skinType)
		{
			switch (skinType)
			{
				case SkinningType.Skinning:
					return SkillId.Skinning;
				case SkinningType.Engineering:
					return SkillId.Engineering;
				case SkinningType.Herbalism:
					return SkillId.Herbalism;
				case SkinningType.Mining:
					return SkillId.Mining;
			}
			return SkillId.Skinning;
		}

		#region Packets
		[ClientPacketHandler(RealmServerOpCode.CMSG_UNLEARN_SKILL)]
		public static void HandleUnlearnSkill(IRealmClient client, RealmPacketIn packet)
		{
			uint skillId = packet.ReadUInt32();

			client.ActiveCharacter.Skills.Remove((SkillId)skillId);
		}
		#endregion
	}
}
